/*
 * Copyright (c) 2020, Armink, <armink.ztl@gmail.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <string.h>
#include <fal.h>
#include <stm32f10x.h>
#include "sfud.h"
#include "flashdb.h"
#include "os.h"

/* STM32F10xRE 内部FLASH */
#define PAGE_SIZE           (2048)
#define FLASH_PAGE_SIZE     (0x800U)

#define INIT_CHECK() \
if(sfud_dev==NULL) return -1;\
if(!sfud_dev->init_ok) return -2;\
        
//#define LOCKER_ENABLE

void feed_dog(void);

/* 内部FLASH */
static int init(void);
static int read(long offset, uint8_t *buf, size_t size);
static int write(long offset, const uint8_t *buf, size_t size);
static int erase(long offset, size_t size);

/* 外部FLASH */
static int spi_flash_init(void);
static int spi_flash_read(long offset, uint8_t *buf, size_t size);
static int spi_flash_write(long offset, const uint8_t *buf, size_t size);
static int spi_flash_erase(long offset, size_t size);

static fdb_time_t get_time(void);
static void lock(fdb_db_t db);
static void unlock(fdb_db_t db);


volatile T_MUTEX xMutexFDBFlash=NULL; //MUTEX_FDBFlash
static sfud_flash_t sfud_dev = NULL;

/* KVDB object */
static struct fdb_kvdb kvdb = { 0 };

/* TSDB object */
//struct fdb_tsdb tsdb = { 0 };

/* counts for simulated timestamp */
static int counts = 0;

#pragma pack(1)
typedef struct{
     uint8_t SOC;
    uint16_t AhSum;
    uint32_t ChgCyc;
}app_data_st;
#pragma pack()

app_data_st app_data={0};

static int ef_err_port_cnt = 0;
int on_ic_read_cnt  = 0;
int on_ic_write_cnt = 0;

static uint8_t fdb_init_flg=0;

static uint32_t boot_count = 0;
static time_t   boot_time[10] = {0, 1, 2, 3};


/*
  "stm32_onchip" : Flash 设备的名字。
  0x08000000     : 对 Flash 操作的起始地址。
  1024*1024      ：Flash 的总大小（1MB）。
  128*1024       ：Flash 块/扇区大小（因为 STM32F2 各块大小不均匀，所以擦除粒度为最大块的大小：128K）。
  {init, read, write, erase} ：Flash 的操作函数。 如果没有 init 初始化过程，第一个操作函数位置可以置空。
  8 : 设置写粒度，单位 bit， 0 表示未生效（默认值为 0 ），该成员是 fal 版本大于 0.4.0 的新增成员。
      各个 flash 写入粒度不尽相同，可通过该成员进行设置，以下列举几种常见 Flash 写粒度：
  nor flash:  1 bit
  stm32f2/f4: 8 bit
  stm32f1:    32 bit
  stm32l4:    64 bit
 */


//1. 内部 flash 设备
const struct fal_flash_dev stm32_onchip_flash =
{
    .name       = "stm32_onchip",
    .addr       = 0x08000000,
    .len        = 512*1024,
    .blk_size   = 2*1024,
    .ops        = {init, read, write, erase},
    .write_gran = 32
};

//2. 外部 flash 设备
struct fal_flash_dev by25q64_flash =
{
    .name       = NOR_FLASH_NAME,
    .addr       = 0,
    .len        = NOR_FLASH_CAPACITY_SIZE,    /* FLASH大小   */
    .blk_size   = NOR_FLASH_SECTOR_SIZE,      /* 块/扇区大小 */
    .ops        = {spi_flash_init,  \
                   spi_flash_read,  \
                   spi_flash_write, \
                   spi_flash_erase},
    .write_gran = 1
};

/* default KV nodes */
static struct fdb_default_kv_node default_kv_table[] = {
        {"username"  , "armink",    0                 }, /* string KV */
        {"password"  , "123456",    0                 }, /* string KV */
        {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
        {"boot_time" , &boot_time,  sizeof(boot_time) }, /* int array type KV */
};

#if 0
/* default KV nodes */
static struct fdb_default_kv_node onchip_kv_table[] = {
        {"username"  , "armink",    0                 }, /* string KV */
        {"password"  , "123456",    0                 }, /* string KV */
        {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
        {"boot_time" , &boot_time,  sizeof(boot_time) }, /* int array type KV */
};

/* default KV nodes */
static struct fdb_default_kv_node kvdb_def_param_tab[] = {
        {"username"  , "armink",    0                 }, /* string KV */
        {"password"  , "123456",    0                 }, /* string KV */
        {"boot_count", &boot_count, sizeof(boot_count)}, /* int type KV */
        {"boot_time" , &boot_time,  sizeof(boot_time) }, /* int array type KV */
};
#endif


/**
@brief 当启动开门狗时，长时间进行FLASH读写时，为防止复位，需要进行喂狗操作
**/
void feed_dog(void)
{

}

static int init(void)
{
    /* do nothing now */
    return 1;
}

static void lock(fdb_db_t db)
{
#if 1 
    MUTEX_LOCK(xMutexFDBFlash, MUTEX_WAIT_ALWAYS);
#else
    __disable_irq();
#endif
}

static void unlock(fdb_db_t db)
{
#if 1
    MUTEX_UNLOCK(xMutexFDBFlash);
#else
    __enable_irq();
#endif
}

static fdb_time_t get_time(void)
{
    /* Using the counts instead of timestamp.
     * Please change this function to return RTC time.
     */
    return ++counts;
}

/**
@brief 外设FLASH芯片初始化
**/
static int spi_flash_init(void)
{
    if (sfud_init() == SFUD_SUCCESS)
    {
        /* enable qspi fast read mode, set four data lines width */
        sfud_dev = sfud_get_device(SFUD_BY25Q64AS_DEVICE_INDEX);
        //sfud_qspi_fast_read_enable(sfud_dev, 4);
    }	

    if (NULL == sfud_dev)
    {
        return -1;
    }

    /* update the flash chip information */
    by25q64_flash.blk_size = sfud_dev->chip.erase_gran;
    by25q64_flash.len      = sfud_dev->chip.capacity;

    return 0;
}

/**
@brief 外设FLASH芯片-读操作
**/
static int spi_flash_read(long offset, uint8_t *buf, size_t size)
{
    INIT_CHECK();
    assert(sfud_dev);
    assert(sfud_dev->init_ok);
    sfud_read(sfud_dev, by25q64_flash.addr + offset, size, buf);

    return size;
}

/**
@brief 外设FLASH芯片-写操作
**/
static int spi_flash_write(long offset, const uint8_t *buf, size_t size)
{
    INIT_CHECK();
    assert(sfud_dev);
    assert(sfud_dev->init_ok);
    if (sfud_write(sfud_dev, by25q64_flash.addr + offset, size, buf) != SFUD_SUCCESS)
    {
        return -1;
    }

    return size;
}

/**
@brief 外设FLASH芯片-擦除操作
**/
static int spi_flash_erase(long offset, size_t size)
{
    INIT_CHECK();
    assert(sfud_dev);
    assert(sfud_dev->init_ok);
    if (sfud_erase(sfud_dev, by25q64_flash.addr + offset, size) != SFUD_SUCCESS)
    {
        return -1;
    }

    return size;
}


#if 1
/**
@brief MCU内部FLASH读操作
**/
static int read(long offset, uint8_t *buf, size_t size)
{
    size_t i;
    uint32_t addr = stm32_onchip_flash.addr + offset;

    if( addr%4 != 0)
        ef_err_port_cnt++;

    for (i = 0; i < size; i++, addr++, buf++)
    {
        *buf = *(uint8_t *) addr;
    }
    on_ic_read_cnt++;
    
    return size;
}
#else //外部FLASH
static int read(long offset, uint8_t *buf, size_t size)
{
    //Xassert(size % 4 == 0);
 
    /* You can add your code under here. */
    int32_t ret;
    uint32_t addr = nor_flash0.addr + offset;
 
    
    LOCK();
	ret = flash_read(0, addr, buf, size);
    UNLOCK();
 
    return ret;
}
#endif

#if 1
/**
@brief MCU内部FLASH写操作
**/
static int write(long offset, const uint8_t *buf, size_t size)
{
    size_t   i;
    uint32_t addr = stm32_onchip_flash.addr + offset;

    __align(4) uint32_t write_data;
    __align(4) uint32_t read_data;  

    if(addr%4 != 0)
        ef_err_port_cnt++;

    FLASH_Unlock();
    for (i = 0; i < size; i += 4, buf+=4, addr += 4) {
        memcpy(&write_data, buf, 4); //用以保证HAL_FLASH_Program的第三个参数是内存首地址对齐
        FLASH_ProgramWord(addr, write_data);
        read_data = *(uint32_t *)addr;
        /* You can add your code under here. */
        if (read_data != write_data) {
            FLASH_Lock();
            return -1;
        }
        else{
			//FLash操作可能非常耗时，如果有看门狗需要喂狗，以下代码由用户实现
           feed_dog();
        }
    }
    FLASH_Lock();

    on_ic_write_cnt++;

    return size;
}
#else
static int write(long offset, const uint8_t *buf, size_t size)
{
    int32_t ret;
    uint32_t addr = nor_flash0.addr + offset;
    
    LOCK();
    ret = flash_write(0, addr, buf, size);
    UNLOCK();
    
    return ret;
}
#endif


#if 1
/**
@brief MCU内部FLASH擦除操作
**/
static int erase(long offset, size_t size)
{
    uint32_t addr = stm32_onchip_flash.addr + offset;
    uint32_t page_address=0;
    size_t erase_pages, i;
    FLASH_Status status;
    
    erase_pages = size / PAGE_SIZE;
    if (size % PAGE_SIZE != 0) {
        erase_pages++;
    }

    FLASH_Unlock();
    for (i = 0; i < erase_pages; i++) {

        page_address = addr + (FLASH_PAGE_SIZE * i);
        status = FLASH_ErasePage(page_address);
        if(status!=FLASH_COMPLETE){
            FLASH_Lock();
            return -1;
        }
        else{
			//FLash操作可能非常耗时，如果有看门狗需要喂狗，以下代码由用户实现
            feed_dog();
        }
    }
    FLASH_Lock();

    return size;
}
#else
static int erase(long offset, size_t size)
{
    int32_t ret;
    uint32_t addr = nor_flash0.addr + offset;
 
    int32_t erase_size = ((size - 1) / FLASH_ERASE_MIN_SIZE) + 1;
 
    LOCK();
    ret = flash_erase(0, addr, erase_size * FLASH_ERASE_MIN_SIZE);
    UNLOCK();
    
    return ret;
}
#endif


#define FDB_LOG_TAG "[sample][tsdb]"

/**
@brief  FlashDB初始化
@return -1=初始化失败
**/
fdb_err_t fdb_init(fdb_kvdb_t *handle)
{
    fdb_err_t err=FDB_NO_ERR;
    
    if(fdb_init_flg)
    {
        *handle=&kvdb;
        return err;
    }
    
    fdb_init_flg=1;
    MUTEX_CREATE(xMutexFDBFlash);
    
    struct fdb_default_kv default_kv;
    
    /* 匹配键-值表 */
    default_kv.kvs = default_kv_table;
    default_kv.num = sizeof(default_kv_table) / sizeof(default_kv_table[0]);
    
    /* set the lock and unlock function if you want */
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_LOCK,   (void *)lock  );
    fdb_kvdb_control(&kvdb, FDB_KVDB_CTRL_SET_UNLOCK, (void *)unlock);

    /* 关联分区表 */
    err = fdb_kvdb_init(&kvdb, "env", "fdb_kvdb1", &default_kv, NULL);
    if (err != FDB_NO_ERR) {
        FDB_INFO("KVDB init failed. resoult = %d\n",err);
        return err;
    }
    *handle=&kvdb;
    return err;
}

/**
@brief 采用键-值方式设置
@param handle-分区表句柄
@param key-键
@param pdata-值
@param len-写入数据长度
@return fdb_err_t状态
**/
fdb_err_t fdb_set_value(fdb_kvdb_t handle, const char *key, uint8_t *pdata, uint16_t len)
{
    fdb_err_t err;
    struct fdb_blob blob;
    
    err = fdb_kv_set_blob(handle, key, fdb_blob_make(&blob, pdata, len));

    return err;
}

/**
@brief 采用键-值方式读数据
@param handle-分区表句柄
@param key-键
@param pdata-值
@param len-写入数据长度
@return 0=没有找到key, 非0=读取的数据长度
**/
size_t fdb_get_value(fdb_kvdb_t handle, const char *key, uint8_t *pdata, uint16_t len)
{
    struct fdb_blob blob;
    size_t read_len;
    
    read_len = fdb_kv_get_blob(handle, key, fdb_blob_make(&blob, pdata, len));
    
    return read_len;
}

/**************************************以下为测试函数**************************************/

uint8_t debug_meter_parm=0;

/**
@brief key 写数
**/
void fdb_key_write(void)
{
    size_t len;
    
    len = fdb_get_value(&kvdb, "debug_meter_parm", (uint8_t *)&debug_meter_parm, sizeof(debug_meter_parm));
    if(0==len)
    {
        FDB_INFO("first powr on init....\r\n");
        debug_meter_parm=5;
        fdb_set_value(&kvdb, "debug_meter_parm", (uint8_t *)&debug_meter_parm, sizeof(debug_meter_parm));
        FDB_INFO("set default value success!\r\n");
    }
    
//    fdb_get_value(&kvdb, "fdb_kvdb1", (uint8_t *)&app_data, sizeof(app_data));
//    FDB_INFO("===========================================================\n");
//    app_data.SOC   +=1;
//    app_data.AhSum +=2;
//    app_data.ChgCyc+=3;
//    fdb_set_value(&kvdb, "fdb_kvdb1", (uint8_t *)&app_data, sizeof(app_data));
//    FDB_INFO("===========================================================\n");
    
    fdb_get_value(&kvdb, "debug_meter_parm", (uint8_t *)&debug_meter_parm, sizeof(debug_meter_parm));
    FDB_INFO("get debug_meter_parm = %d\r\n",debug_meter_parm);
    
    debug_meter_parm+=1;
    fdb_set_value(&kvdb, "debug_meter_parm", (uint8_t *)&debug_meter_parm, sizeof(debug_meter_parm));
    FDB_INFO("set debug_meter_parm = %d\r\n",debug_meter_parm);    
}
